package com.gitee.neuray.security.generator.strategy;

import com.alibaba.fastjson.JSONObject;
import com.gitee.neuray.security.common.exception.BaseException;
import com.gitee.neuray.security.generator.utils.DateUtils;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author: zhhongyu
 * @description: 代码生成策略加模板生成方法
 * @since: Create in 13:12 2020/2/19
 */
public abstract class GenerateStrategy {
  /**
   * 生成代码
   *
   * @param bizParams 基本的业务参数
   * @param table 表基本属性名称（tableName，tableComment）
   * @param columns 列的基本属性名称（columnName，dataType，columnComment，extra）
   * @param zip 压缩文件流
   */
  public void start(
      GenBaseBizEntityStrategy bizParams,
      Map<String, String> table,
      List<Map> columns,
      ZipOutputStream zip) {
    // 配置信息
    Configuration config = getConfig();
    VelocityContext context = getVelocityContext(config, bizParams, table, columns);
    // 获取模板列表
    List<String> templates = getTemplates();
    String tableName = table.get("tableName");
    String className = tableToJava(tableName, bizParams.getTablePrefix());
    for (String template : templates) {
      // 渲染模板
      StringWriter sw = new StringWriter();
      Template tpl = Velocity.getTemplate(template, "UTF-8");
      tpl.merge(context, sw);
      try {
        // 添加到zip
        zip.putNextEntry(
            new ZipEntry(
                getFileName(
                    template,
                    className,
                    bizParams.getJavaPath(),
                    bizParams.getModuleName(),
                    bizParams.getVuePath(),
                    bizParams.getServerName())));
        IOUtils.write(sw.toString(), zip, "UTF-8");
        IOUtils.closeQuietly(sw);
        zip.closeEntry();
      } catch (IOException e) {
        throw new BaseException("渲染模板失败，表名：" + tableName, e);
      }
    }
  }

  /**
   * 获取模板参数
   *
   * @param config 系统参数映射配置文件
   * @param bizParams 业务参数
   * @param table 表基本属性
   * @param columns 列的基本属性
   * @return
   */
  protected abstract VelocityContext getVelocityContext(
      Configuration config,
      GenBaseBizEntityStrategy bizParams,
      Map<String, String> table,
      List<Map> columns);

  /**
   * 获取基类map视图
   *
   * @param tableEntity
   * @return
   */
  protected Map<String, Object> getMap(GenBaseTableEntityStrategy tableEntity) {
    Map<String, Object> map = new HashMap<>();
    map.put("tableName", tableEntity.getTableName());
    if (tableEntity.getComments() == null || Objects.equals(tableEntity.getComments(), "")) {
      map.put("comments", "代码生成");
    } else {
      map.put("comments", tableEntity.getComments());
    }
    map.put("className", tableEntity.getClassName());
    map.put("classname", tableEntity.getClassname());
    map.put("pathName", tableEntity.getClassname().toLowerCase());
    map.put("package", tableEntity.getJavaPath());
    map.put("moduleName", tableEntity.getModuleName());
    map.put("serverName", tableEntity.getServerName());
    map.put("author", tableEntity.getAuthor());
    map.put("email", tableEntity.getEmail());
    map.put("datetime", DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
    map.put("gName", tableEntity.getGName()); // add by liln 2020-03-19
    return map;
  }

  /**
   * 获取tableentity
   *
   * @param bizParams
   * @param table
   * @return
   */
  protected GenBaseTableEntityStrategy getTableEntity(
      GenBaseBizEntityStrategy bizParams, Map<String, String> table) {
    // 表信息
    GenBaseTableEntityStrategy tableEntity =
        JSONObject.parseObject(
            JSONObject.toJSONString(bizParams), GenBaseTableEntityStrategy.class);
    tableEntity.setTableName(table.get("tableName"));
    tableEntity.setComments(table.get("tableComment"));
    // 表名转换成Java类名
    String className = tableToJava(tableEntity.getTableName(), bizParams.getTablePrefix());
    tableEntity.setClassName(className);
    tableEntity.setClassname(StringUtils.uncapitalize(className));
    return tableEntity;
  }

  /** 获取配置信息 */
  protected Configuration getConfig() {
    try {
      return new PropertiesConfiguration("genstrategy.properties");
    } catch (ConfigurationException e) {
      throw new BaseException("获取配置文件失败，", e);
    }
  }

  /** 列名转换成Java属性名 */
  protected String columnToJava(String columnName) {
    return WordUtils.capitalizeFully(columnName, new char[] {'_'}).replace("_", "");
  }

  /** 表名转换成Java类名 */
  protected String tableToJava(String tableName, String tablePrefix) {
    if (StringUtils.isNotBlank(tablePrefix)) {
      tableName = tableName.replace(tablePrefix, "");
    }
    return columnToJava(tableName);
  }

  /**
   * 获取所有vm模板
   *
   * @return
   */
  protected abstract List<String> getTemplates();

  /**
   * 获取文件名
   *
   * @param template
   * @param className
   * @param packageName
   * @param moduleName
   * @return
   */
  protected abstract String getFileName(
      String template,
      String className,
      String packageName,
      String moduleName,
      String vuePath,
      String serverName);

  /**
   * 获取column
   *
   * @param columns
   * @param config
   * @param tableEntity
   * @param hasBigDecimal
   * @return
   */
  protected List<GenBaseColumnEntityStrategy> getColumns(
      List<Map> columns,
      Configuration config,
      GenBaseTableEntityStrategy tableEntity,
      boolean hasBigDecimal) {
    // 列信息
    List<GenBaseColumnEntityStrategy> columsList = new ArrayList<>();
    for (Map<String, String> column : columns) {
      GenBaseColumnEntityStrategy columnEntity = new GenBaseColumnEntityStrategy();
      columnEntity.setColumnName(column.get("columnName"));
      columnEntity.setDataType(column.get("dataType"));
      columnEntity.setComments(column.get("comments"));
      columnEntity.setExtra(column.get("extra"));

      // 列名转换成Java属性名
      String attrName = columnToJava(columnEntity.getColumnName());
      columnEntity.setAttrName(attrName);
      columnEntity.setAttrname(StringUtils.uncapitalize(attrName));

      // 列的数据类型，转换成Java类型
      String attrType = config.getString(columnEntity.getDataType(), "unknowType");
      columnEntity.setAttrType(attrType);
      if (!hasBigDecimal && attrType.equals("BigDecimal")) {
        hasBigDecimal = true;
      }
      // 是否主键
      if ("PRI".equalsIgnoreCase(column.get("columnKey")) && tableEntity.getPk() == null) {
        tableEntity.setPk(columnEntity);
      }
      columsList.add(columnEntity);
    }
    return columsList;
  }

  /**
   * 获取vue的路径
   *
   * @return
   */
  protected List<String> getVueTemplate() {
    List<String> templates = new ArrayList<String>();
    templates.add("template/vm2.vue.vm");
    return templates;
  }

  protected Map<String, Object> getVueContextMap(
      Configuration config, GenBaseBizEntityStrategy bizEntity) {
    Map<String, Object> result = new HashMap<>();
    List<GenBaseColumnEntityStrategy> columns = bizEntity.getTable();
    List<GenBaseColumnEntityStrategy> queryColumns =
        columns.parallelStream().filter(entity -> entity.getQueryIf()).collect(Collectors.toList());
    List<GenBaseColumnEntityStrategy> listColumns =
        columns.parallelStream().filter(entity -> entity.getListIf()).collect(Collectors.toList());
    foreachList(config, queryColumns);
    foreachList(config, listColumns);
    result.put("queryColumns", queryColumns);
    result.put("listColumns", listColumns);
    StringBuilder url = new StringBuilder();
    url.append("/");
    url.append(bizEntity.getServerName());
    url.append("/");
    url.append(bizEntity.getModuleName());
    result.put("url", url.toString());
    result.put("methods", Arrays.asList(bizEntity.getMethods()));
    if (Arrays.asList(bizEntity.getMethods()) != null
        && Arrays.asList(bizEntity.getMethods()).contains("export")) {
      result.put("isExport", true);
    } else {
      result.put("isExport", false);
    }
    return result;
  }

  private void foreachList(Configuration config, List<GenBaseColumnEntityStrategy> columns) {
    for (GenBaseColumnEntityStrategy columnEntity : columns) {
      // 列名转换成Java属性名
      String attrName = columnToJava(columnEntity.getColumnName());
      columnEntity.setAttrName(attrName);
      columnEntity.setAttrname(StringUtils.uncapitalize(attrName));
      // 列的数据类型，转换成Java类型
      String attrType = config.getString(columnEntity.getDataType(), "unknowType");
      columnEntity.setAttrType(attrType);
    }
  }
}
